package co.edu.uniandes.distribuidos.fss.adv;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class FileID implements Serializable, Comparable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -6490332952848544359L;

	private byte[] bytes;
	private String peerID;

	// content id type
	private static final String ID_TYPE = "md5";

	public FileID() {
	}

	/*
	 * Creates a new content id from the specified identifier string.
	 * The identifier string is of the form <idtype> ":" <hexstring>
	 * where <idtype> is the identifier type (currently 'md5') and
	 * <hexstring> the hash hex bytes.
	 */
	public FileID(String s) {
		bytes = parse(s);
	}

	/*
	 * Creates a new content id for the bytes contained in the specified
	 * input stream.
	 */
	public FileID(InputStream is, String peerID) throws IOException {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException(
					"Could not find MD5 MessageDigest");
		}
		byte[] b = new byte[1024];
		int len;
		while ((len = is.read(b, 0, b.length)) != -1) {
			md.update(b, 0, len);
		}
		bytes = md.digest();
		this.peerID = peerID;
	}

	// Parse content id string
	private byte[] parse(String s) {
		int pos = s.indexOf(':');
		int posDos = s.indexOf("::");
		if (pos == -1) {
			throw new IllegalArgumentException("Missing content id type: " + s);
		}
		if (posDos == -1) {
			throw new IllegalArgumentException("Missing content id peer: " + s);
		}
		String type = s.substring(0, pos);
		if (!type.equalsIgnoreCase(ID_TYPE)) {
			throw new IllegalArgumentException("Invalid content id type: " + s);
		}
		peerID = s.substring(posDos+2,s.length());
		/*if (s.length() - pos - 1 != 32) {
			throw new IllegalArgumentException(
					"Invalid content id hash length: " + s);
		}*/
		s = s.substring(pos + 1,posDos);
		byte[] b = new byte[16];
		for (int i = 0, j = 0; i < 16; i++) {
			int hi = Character.digit(s.charAt(j++), 16);
			int lo = Character.digit(s.charAt(j++), 16);
			if (hi == -1 || lo == -1) {
				throw new IllegalArgumentException(
						"Invalid content id hash: " + s);
			}
			b[i] = (byte)((hi << 4) | lo);
		}
		return b;
	}

	/**
	 * Returns the hash code for this content id.
	 */
	public int hashCode() {
		byte[] b = bytes;
		return ((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) |
		((b[2] & 0xff) <<  8) | ((b[3] & 0xff) <<  0);
	}

	/**
	 * Returns true if this content id is equal to the specified object.
	 */
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof FileID)) {
			return false;
		}
		FileID id = (FileID)obj;
		byte[] b1 = bytes;
		byte[] b2 = id.bytes;
		if (b1.length != b2.length) {
			return false;
		}
		for (int i = 0; i < b1.length; i++) {
			if (b1[i] != b2[i]) {
				return false;
			}
		}
		if(!peerID.equals(((FileID)obj).getPeerID()))
			return false;
		return true;
	}

	/**
	 * Returns the string representation of this content id.
	 */
	public String toString() {
		byte[] b = bytes;
		StringBuffer sb = new StringBuffer(ID_TYPE.length() + 1 + b.length * 2);
		sb.append(ID_TYPE);
		sb.append(':');
		for (int i = 0; i < b.length; i++) {
			int hi = (b[i] >> 4) & 0xf;
			int lo = b[i] & 0xf;
			sb.append(Character.forDigit(hi, 16));
			sb.append(Character.forDigit(lo, 16));
		}
		sb.append("::");
		sb.append(peerID);
		return sb.toString();
	}
	public FileID clonar(){
		FileID fileID = new FileID();
		fileID.setBytes(bytes);
		fileID.setPeerID(peerID);
		return fileID;
	}

	public byte[] getBytes() {
		return bytes;
	}

	public void setBytes(byte[] bytes) {
		this.bytes = bytes;
	}

	public String getPeerID() {
		return peerID;
	}

	public void setPeerID(String peerID) {
		this.peerID = peerID;
	}

	@Override
	public int compareTo(Object o) {
		if (o==null)
			return -1;
		else if (this.toString().equals(((FileID)o).toString()))
			return 1;
		else
			return 0;
	}
	
	
}

	